package com.maycur.ctrip.service.impl;


import com.maycur.ctrip.domain.*;
import com.maycur.ctrip.dto.*;
import com.maycur.ctrip.enumeration.PayType;
import com.maycur.ctrip.exception.ApiException;
import com.maycur.ctrip.mybatis.mapper.*;
import com.maycur.ctrip.service.OrderService;
import com.maycur.ctrip.service.UserService;
import com.maycur.ctrip.util.Constant;
import com.maycur.ctrip.util.CtripApiUtil;
import com.maycur.ctrip.util.HttpUtil;
import com.maycur.ctrip.util.MaycurIDProduceUtil;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.List;



/**
 * Created by JoeyXin on 6/23/16.
 */
@Service
@Transactional
public class OrderServiceImpl implements OrderService {
    private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);

    @Autowired
    private CtripApiUtil apiUtil;

    @Autowired
    private MaycurIDProduceUtil maycurIDProduceUtil;

    @Autowired
    private HotelOrderMapper hotelOrderMapper;

    @Autowired
    private ClientInfoMapper clientInfoMapper;

    @Autowired
    private HotelRoomMapper hotelRoomMapper;

    @Autowired
    private FlightOrderMapper flightOrderMapper;

    @Autowired
    private FlightTicketMapper flightTicketMapper;

    @Autowired
    private TrainOrderMapper trainOrderMapper;

    @Autowired
    private TrainTicketMapper trainTicketMapper;

    @Autowired
    private FeeDataMapper feeDataMapper;

    @Autowired
    private CurrencyMapper currencyMapper;

    @Autowired
    private UserService userService;

    @Override
    public List<ItineraryEntity> fetchOrdersFromCtrip(String entCode, FetchCtripOrderRequest searchOrderReq){
        searchOrderReq.setAuth(apiUtil.getAccessAuth(entCode, false));
        FetchCtripOrderResponse
            response = HttpUtil.httpPost(Constant.SEARCH_ORDER_URL, searchOrderReq, FetchCtripOrderResponse.class);

        validateResponse(response, searchOrderReq);

        return response.getItineraryEntities();
    }

    @Override
    public List<String> listHotelOrderIdInDb(OrderSearchCriteria searchCriteria) {
        return hotelOrderMapper.selectOrderIds(searchCriteria);
    }

    @Override
    public List<String> listFlightOrderIdInDb(OrderSearchCriteria searchCriteria) {
        return flightOrderMapper.selectOrderIds(searchCriteria);
    }

    @Override
    public List<String> listTrainOrderIdInDb(OrderSearchCriteria searchCriteria) {
        return trainOrderMapper.selectOrderIds(searchCriteria);
    }

    private void validateResponse(HttpResponse httpResponse, FetchCtripOrderRequest requestEvent) throws ApiException {
        if(httpResponse == null) {
            logger.error("Get null response from Ctrip for request ({}) !",requestEvent);
            throw new ApiException("Get null response from Ctrip");
        }else if(!httpResponse.getResponseStatus().getSuccess() || httpResponse.getResponseStatus().getErrorCode() > 0) {
            logger.error("Get error response(code: {}, message: {}) for request ({}) !", httpResponse.getResponseStatus().getErrorCode(), httpResponse.getResponseStatus().getMessage(), requestEvent);
            throw new ApiException("Get error response(code: " + httpResponse.getResponseStatus().getErrorCode() + ", message: " + httpResponse.getResponseStatus().getMessage() + ") from Ctrip");
        }
    }

    @Override
    public boolean syncItineraryListToMaycur(String entCode, String userCode, List<ItineraryEntity> itineraryEntities) {
        if(CollectionUtils.isEmpty(itineraryEntities)) {
            return true;
        }

        for(ItineraryEntity entity: itineraryEntities) {
            syncHotelOrders(entCode, userCode, entity.getHotelOrderInfoEntities());
            syncFlightOrders(entCode, userCode, entity.getFlightOrderInfoEntities());
            syncTrainOrders(entCode, userCode, entity.getTrainOrderInfoEntities());
        }
        return false;
    }

    private boolean syncHotelOrders(String entCode, String userCode, List<HotelOrderInfoEntity> hotelOrderInfoEntities) throws ApiException{
        if(CollectionUtils.isEmpty(hotelOrderInfoEntities)) {
            return true;
        }

        for(HotelOrderInfoEntity hotelOrderInfoEntity: hotelOrderInfoEntities) {
            HotelOrder hotelOrder = new HotelOrder();
            try {
                PropertyUtils.copyProperties(hotelOrder, hotelOrderInfoEntity);
                hotelOrder.setEntCode(entCode);
                hotelOrder.setSyncedToFee(true);
                hotelOrderMapper.insert(hotelOrder);

                for(HotelClientInfoEntity hotelClientInfoEntity: hotelOrderInfoEntity.getClientInfoEntities()) {
                    ClientInfo clientInfo = new ClientInfo();
                    PropertyUtils.copyProperties(clientInfo, hotelClientInfoEntity);
                    clientInfo.setOrderId(hotelOrder.getOrderId());
                    clientInfo.setClientType(Constant.HOTEL_CLIENT);
                    clientInfoMapper.insert(clientInfo);
                }

                for(HotelRoomInfoEntity hotelRoomInfoEntity: hotelOrderInfoEntity.getRoomInfoEntities()) {
                    HotelRoom hotelRoom = new HotelRoom();
                    PropertyUtils.copyProperties(hotelRoom, hotelRoomInfoEntity);
                    hotelRoom.setOrderId(hotelOrder.getOrderId());
                    hotelRoomMapper.insert(hotelRoom);
                }

                String feeCode = maycurIDProduceUtil.produceFeeDataCode();
                FeeData fee = hotelOrderInfoEntity.extractFee(entCode, feeCode, userCode);

                updateSubCodeAndAmounts(entCode, userCode, fee);
                feeDataMapper.insertFee(fee);
                feeDataMapper.insertFeeDynamicFields(hotelOrderInfoEntity.extractFeeDynamicFields(entCode, feeCode));

            } catch (Exception e) {
                logger.error("Failed to synchronized hotel order (orderId={}) to Maycur due to {}", hotelOrderInfoEntity.getOrderId(), e);
                throw new ApiException("Failed to synchronized hotel order (orderId=" + hotelOrderInfoEntity.getOrderId() + ")" );
            }
        }

        return false;
    }

    private boolean syncFlightOrders(String entCode, String userCode, List<FlightOrderInfoEntity> flightOrderInfoEntities) {
        if(CollectionUtils.isEmpty(flightOrderInfoEntities)) {
            return true;
        }

        for(FlightOrderInfoEntity flightOrderInfoEntity: flightOrderInfoEntities) {
            FlightOrder flightOrder = new FlightOrder();

            try {
                FlightOrderBasicInfo flightOrderBasicInfo = flightOrderInfoEntity.getBasicInfo();
                PropertyUtils.copyProperties(flightOrder, flightOrderBasicInfo);
                flightOrder.setEntCode(entCode);
                flightOrder.setSyncedToFee(true);
                flightOrderMapper.insert(flightOrder);

                for(FlightPassengerInfoEntity flightPassengerInfoEntity: flightOrderInfoEntity.getPassengerInfoEntities()) {
                    FlightPassengerBasicInfo passengerBasicInfo = flightPassengerInfoEntity.getPassengerBasicInfo();

                    ClientInfo clientInfo = new ClientInfo();
                    PropertyUtils.copyProperties(clientInfo, passengerBasicInfo);
                    clientInfo.setOrderId(flightOrder.getOrderId());
                    clientInfo.setClientType(Constant.FLIGHT_CLIENT);
                    clientInfoMapper.insert(clientInfo);

                    for(FlightTicketInfo flightTicketInfo: flightOrderInfoEntity.getTicketInfos()) {
                        FlightTicket flightTicket = new FlightTicket();
                        PropertyUtils.copyProperties(flightTicket, flightTicketInfo);
                        flightTicket.setOrderId(flightOrder.getOrderId());
                        flightTicketMapper.insert(flightTicket);

                        String feeCode = maycurIDProduceUtil.produceFeeDataCode();
                        FeeData fee = flightTicketInfo.extractFee(entCode, feeCode, userCode, clientInfo.getClientName());

                        fee.setPayMethod(PayType.fromDesc(flightOrderBasicInfo.getPayType()));
                        fee.setConsumeTime(flightOrderBasicInfo.getOrderedAt());
                        fee.setSourceId(flightOrderBasicInfo.getOrderId());

                        updateSubCodeAndAmounts(entCode, userCode, fee);
                        feeDataMapper.insertFee(fee);

                        feeDataMapper.insertFeeDynamicFields(flightTicketInfo.extractFeeDynamicFields(entCode, feeCode));
                    }
                }

            }catch (Exception e) {
                logger.error("Failed to synchronized flight order (orderId={}) to Maycur due to {}", flightOrderInfoEntity.getBasicInfo().getOrderId(), e);
                throw new ApiException("Failed to synchronized flight order (orderId=" + flightOrderInfoEntity.getBasicInfo().getOrderId() + ")" );
            }

        }
        return false;
    }

    private boolean syncTrainOrders(String entCode, String userCode, List<TrainOrderInfoEntity> trainOrderInfoEntities) {
        if(CollectionUtils.isEmpty(trainOrderInfoEntities)) {
            return true;
        }

        for(TrainOrderInfoEntity trainOrderInfoEntity: trainOrderInfoEntities) {
            TrainOrder trainOrder = new TrainOrder();

            try{
                TrainOrderBasicInfo trainOrderBasicInfo = trainOrderInfoEntity.getBasicInfo();
                PropertyUtils.copyProperties(trainOrder, trainOrderBasicInfo);
                trainOrder.setEntCode(entCode);
                trainOrder.setSyncedToFee(true);
                trainOrder.setConsumeAmount(trainOrderBasicInfo.getOriginalAmount());
                trainOrderMapper.insert(trainOrder);

                for(TrainPassengerInfoEntity trainPassengerInfoEntity: trainOrderInfoEntity.getPassengerInfoEntities()) {
                    ClientInfo clientInfo = new ClientInfo();
                    PropertyUtils.copyProperties(clientInfo, trainPassengerInfoEntity);
                    clientInfo.setOrderId(trainOrder.getOrderId());
                    clientInfo.setClientType(Constant.TRAIN_CLIENT);
                    clientInfo.setClientId(trainPassengerInfoEntity.getPassengerId());
                    clientInfoMapper.insert(clientInfo);

                    for(TrainTicketInfo trainTicketInfo: trainOrderInfoEntity.getTicketInfos()) {
                        TrainTicket trainTicket = new TrainTicket();
                        PropertyUtils.copyProperties(trainTicket, trainTicketInfo);
                        trainTicket.setOrderId(trainOrder.getOrderId());
                        trainTicketMapper.insert(trainTicket);

                        String feeCode = maycurIDProduceUtil.produceFeeDataCode();
                        FeeData fee = trainTicketInfo.extractFee(entCode, feeCode, userCode, clientInfo.getClientName());

                        fee.setPayMethod(PayType.fromDesc(trainOrderBasicInfo.getPayType()));
                        fee.setConsumeTime(trainOrderBasicInfo.getOrderedAt());
                        fee.setSourceId(trainOrderBasicInfo.getOrderId());

                        updateSubCodeAndAmounts(entCode, userCode, fee);
                        feeDataMapper.insertFee(fee);

                        feeDataMapper.insertFeeDynamicFields(trainTicketInfo.extractFeeDynamicFields(entCode, feeCode));
                    }
                }

            }catch (Exception e) {
                logger.error("Failed to synchronized train order (orderId={}) to Maycur due to {}", trainOrderInfoEntity.getBasicInfo().getOrderId(), e);
                throw new ApiException("Failed to synchronized train order (orderId=" + trainOrderInfoEntity.getBasicInfo().getOrderId() + ")" );
            }
        }

        return false;
    }


    private void updateSubCodeAndAmounts(String entCode, String userCode, FeeData fee) {
        SimplifiedUser user = userService.getUser(entCode, userCode);

        if (user != null) {
            String subsidiaryCode = user.getDefaultSubsidiaryCode();
            String baseCcy = user.getDefaultBaseCcy();
            fee.setSubsidiaryCode(subsidiaryCode);
            if (!Constant.DEFAULT_CTRIP_CURRENCY.equals(user.getDefaultBaseCcy())) {
                BigDecimal exchangeRate = getCtripCcy2BaseCcyExchangeRate(entCode, subsidiaryCode, baseCcy);
                if (exchangeRate != null) {
                    fee.setExchangeRate(exchangeRate);
                    fee.setSysExchangeRate(exchangeRate);
                    fee.setFeeAmount(exchangeRate.multiply(fee.getConsumeAmount()));
                    fee.setApprovedAmount(fee.getFeeAmount());
                    fee.setBaseCcy(user.getDefaultBaseCcy());
                }
            }
        }
    }

    private BigDecimal getCtripCcy2BaseCcyExchangeRate(String entCode, String subsidiaryCode, String baseCcy) {
        if (StringUtils.isEmpty(entCode) || StringUtils.isEmpty(subsidiaryCode) || StringUtils.isEmpty(baseCcy)) {
            return null;
        }

        BigDecimal exchangeRate = currencyMapper
            .selectCtripCcy2BaseCCYRate(entCode, subsidiaryCode, Constant.DEFAULT_CTRIP_CURRENCY, baseCcy);

        if (exchangeRate == null) {
            exchangeRate =
                currencyMapper.selectCtripCcy2BaseCCYRate(entCode, null, Constant.DEFAULT_CTRIP_CURRENCY, baseCcy);
        }

        return exchangeRate;
    }

}
